﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Vixen.Framework
{
    /// <summary>Encapsulates variables and methods related to a custom Sprite handler on the XNA Framework.</summary>
    public abstract class Sprite
    {
        #region Local Variables

        private readonly Texture2D _texture;
        private readonly uint _uid;
        private Vector2 _origin = Vector2.Zero;
        private Rectangle _rect;
        private float _rotation, _scale = 1f; // rotation is in radians, scale is a multiplier

        #endregion

        #region Origin enum

        /// <summary>Where on the Sprite the desired location of Origin can be.</summary>
        public enum Origin
        {
            TopLeft,
            TopRight,
            MiddleLeft,
            Center,
            MiddleRight,
            BottomLeft,
            BottomRight,
            Default
        }

        #endregion

        /// <summary>Creates a Sprite and sets variable values to the default values.</summary>
        /// <param name="assetName">The location of the asset to load in the Content project.</param>
        /// <param name="uid">The unique identification number to assign to the Sprite.</param>
        protected Sprite(string assetName, uint uid)
        {
            _texture = Engine.LoadAsset<Texture2D>(assetName);
            _rect = new Rectangle(0, 0, _texture.Width, _texture.Height);
            _uid = uid;
        }

        /// <summary>Creates a Sprite and sets all variables default except coordinates.</summary>
        /// <param name="assetName">The location of the asset to load in the Content project.</param>
        /// <param name="uid">The unique identification number to assign to the Sprite.</param>
        /// <param name="x">The X location of the Sprite.</param>
        /// <param name="y">The Y location of the Sprite.</param>
        protected Sprite(string assetName, uint uid, int x, int y)
        {
            _texture = Engine.LoadAsset<Texture2D>(assetName);
            _rect = new Rectangle(x, y, _texture.Width, _texture.Height);
            _uid = uid;
        }

        /// <summary>Creates a Sprite and allows all Sprite options to be set.</summary>
        /// <param name="assetName">The location of the asset to load in the Content project.</param>
        /// <param name="uid">The unique identification number to assign to the Sprite.</param>
        /// <param name="x">The X location of the Sprite. <i>0</i> is the default value.</param>
        /// <param name="y">The Y location of the Sprite. <i>0</i> is the default value.</param>
        /// <param name="origin">The point of origin on the Sprite. <i>Origin.DEFAULT</i> is the default value.</param>
        /// <param name="rotation">The rotation of the Sprite. <i>0</i> is the default value.</param>
        /// <param name="scale">The size of the Sprite to scale. <i>1</i> is the default value.</param>
        protected Sprite(string assetName, uint uid, int x, int y, Origin origin, float rotation, float scale)
        {
            _texture = Engine.LoadAsset<Texture2D>(assetName);
            _rect = new Rectangle(x, y, _texture.Width, _texture.Height);

            #region Origin Check

            if (origin == Origin.Default)
                SetOrigin(Origin.TopLeft);

            #endregion

            _rotation = rotation;
            _scale = scale;

            _uid = uid;
        }

        /// <summary>The X location of the Sprite.</summary>
        public int X
        {
            get { return _rect.X; }
            set { _rect.X = value; }
        }

        /// <summary>The Y location of the Sprite.</summary>
        public int Y
        {
            get { return _rect.Y; }
            set { _rect.Y = value; }
        }

        /// <summary>The width of the Sprite.</summary>
        public int Width
        {
            get { return _texture.Width; }
        }

        /// <summary>The height of the Sprite.</summary>
        public int Height
        {
            get { return _texture.Height; }
        }

        /// <summary>The size of the Sprite to scale.</summary>
        public float Scale
        {
            get { return _scale; }
            set { _scale = value; }
        }

        /// <summary>The angled rotation of the Sprite (in radians).</summary>
        public float Rotation
        {
            get { return _rotation; }
            set { _rotation = value; }
        }

        /// <summary>The area that the Sprite is taking.</summary>
        public Rectangle Area
        {
            get { return _rect; }
        }

        /// <summary>The unique identification number for the Sprite.</summary>
        public uint UID
        {
            get { return _uid; }
        }

        /// <summary>Draws the Sprite to the screen using default Sprite options.</summary>
        /// <param name="spriteBatch">The SpriteBatch used to draw the Sprite to the screen.</param>
        /// <param name="frontLayer"><i>true</i> indicates the front layer while <i>false</i> indicates the back layer.</param>
        public void Draw(SpriteBatch spriteBatch, bool frontLayer)
        {
            spriteBatch.Draw(_texture, new Vector2(_rect.X, _rect.Y), null, Color.White, _rotation, _origin, _scale,
                             SpriteEffects.None, frontLayer ? 0 : 1);
        }

        /// <summary>Draws the Sprite to the screen while using set Sprite options.</summary>
        /// <param name="spriteBatch">The SpriteBatch used to draw the Sprite to the screen.</param>
        /// <param name="frontLayer"><i>true</i> indicates the front layer while <i>false</i> indicates the back layer.</param>
        /// <param name="color">The tinting to give the Sprite. White draws the Sprite with no tinting.</param>
        /// <param name="spriteEffects">The SpriteEffects to give to the Sprite.</param>
        public void Draw(SpriteBatch spriteBatch, bool frontLayer, Color color, SpriteEffects spriteEffects)
        {
            spriteBatch.Draw(_texture, new Vector2(_rect.X, _rect.Y), null, color, _rotation, _origin, _scale,
                             spriteEffects, frontLayer ? 0 : 1);
        }

        /// <summary>Runs update logic/code of the Sprite.</summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public virtual void Update(GameTime gameTime)
        {
        }

        /// <summary>Runs update logic/code of the Sprite.</summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        /// <param name="prevKs">The previous state of the keyboard (last update call).</param>
        /// <param name="newKs">The current state of the keyboard (this update call).</param>
        public virtual void Update(GameTime gameTime, KeyboardState prevKs, KeyboardState newKs)
        {
        }

        /// <summary>Runs update logic/code of the Sprite.</summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        /// <param name="prevMs">The previous state of the mouse (last update call).</param>
        /// <param name="newMs">The current state of the mouse (this update call).</param>
        public virtual void Update(GameTime gameTime, MouseState prevMs, MouseState newMs)
        {
        }

        /// <summary>Runs update logic/code of the Sprite.</summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        /// <param name="prevKs">The previous state of the keyboard (last update call).</param>
        /// <param name="newKs">The current state of the keyboard (this update call).</param>
        /// <param name="prevMs">The previous state of the mouse (last update call).</param>
        /// <param name="newMs">The current state of the mouse (this update call).</param>
        public virtual void Update(GameTime gameTime, KeyboardState prevKs, KeyboardState newKs, MouseState prevMs,
                                   MouseState newMs)
        {
        }

        /// <summary>Set's the Sprite's point of origin.</summary>
        /// <param name="origin">Where the Sprite's point of origin is located.</param>
        public void SetOrigin(Origin origin)
        {
            switch (origin)
            {
                case Origin.BottomLeft:
                    _origin = new Vector2(0, _texture.Height);
                    break;
                case Origin.BottomRight:
                    _origin = new Vector2(_texture.Width, _texture.Height);
                    break;
                case Origin.Center:
// ReSharper disable PossibleLossOfFraction
                    _origin = new Vector2(_texture.Width/2, _texture.Height/2);
                    break;
                case Origin.MiddleLeft:
                    _origin = new Vector2(0, _texture.Height/2);
                    break;
                case Origin.MiddleRight:
                    _origin = new Vector2(_texture.Width, _texture.Height/2);
// ReSharper restore PossibleLossOfFraction
                    break;
                case Origin.TopLeft:
                    _origin = new Vector2(0, 0);
                    break;
                case Origin.TopRight:
                    _origin = new Vector2(_texture.Width, 0);
                    break;
            }
        }

        /// <summary>Checks to see if this Sprite intersects (or collides) with another.</summary>
        /// <param name="sprite">The Sprite to check collision for.</param>
        public bool Intersects(Sprite sprite)
        {
            if (_rect.Intersects(sprite.Area))
                return true;
            return false;
        }
    }
}